package com.qiyi.player.core.video.engine.dm
{
   import com.qiyi.player.core.video.engine.BaseEngine;
   import com.qiyi.player.core.video.file.IFile;
   import com.qiyi.player.core.video.provider.IProvider;
   import com.qiyi.player.core.video.engine.dm.agents.RateAgent;
   import com.qiyi.player.core.video.engine.dm.agents.MemoryAgent;
   import com.qiyi.player.core.model.IMovie;
   import com.qiyi.player.core.video.render.IRender;
   import com.qiyi.player.core.player.def.StatusEnum;
   import com.qiyi.player.base.pub.EnumItem;
   import com.qiyi.player.core.model.events.MovieEvent;
   import flash.utils.clearTimeout;
   import com.qiyi.player.core.video.decoder.DataModeDecoder;
   import com.qiyi.player.core.player.def.PlayerUseTypeEnum;
   import com.qiyi.player.core.model.def.PlayerTypeEnum;
   import com.qiyi.player.core.Config;
   import flash.events.NetStatusEvent;
   import com.qiyi.player.core.video.events.DecoderEvent;
   import com.qiyi.player.core.video.events.ProviderEvent;
   import com.qiyi.player.core.video.file.clientp2pfile.ClientP2PFile;
   import com.qiyi.player.core.video.provider.clientp2pprovider.ClientP2PProvider;
   import com.qiyi.player.core.video.file.clientp2pfile.IClientP2PFile;
   import com.qiyi.player.core.video.file.flashp2pfile.FlashP2PFile;
   import com.qiyi.player.core.video.provider.flashp2pprovider.FlashP2PProvider;
   import com.qiyi.player.core.video.file.flashp2pfile.IFlashP2PFile;
   import com.qiyi.player.core.video.file.corefile.CoreFile;
   import com.qiyi.player.core.video.provider.coreprovider.CoreProvider;
   import com.qiyi.player.core.video.file.corefile.ICoreFile;
   import com.qiyi.player.core.video.def.StopReasonEnum;
   import com.qiyi.player.core.video.def.DecoderStatusEnum;
   import flash.utils.setTimeout;
   import com.qiyi.player.core.video.events.EngineEvent;
   import flash.events.Event;
   import com.qiyi.player.core.video.provider.flashp2pprovider.IFlashP2PProvider;
   import com.qiyi.player.core.video.events.RateAgentEvent;
   import flash.events.TimerEvent;
   import com.qiyi.player.core.video.file.actors.datatile.MediaData;
   import com.qiyi.player.core.player.coreplayer.ICorePlayer;
   
   public class DMEngine extends BaseEngine
   {
      
      public function DMEngine(param1:ICorePlayer) {
         super(param1);
         this._rateAgent = new RateAgent(_holder);
         this._rateAgent.addEventListener(RateAgentEvent.Evt_AudioTrackChanged,this.onAudioTrackChanged);
         this._rateAgent.addEventListener(RateAgentEvent.Evt_DefinitionChanged,this.onDefinitionChanged);
         this._memAgent = new MemoryAgent(_holder);
      }
      
      private var _file:IFile;
      
      private var _provider:IProvider;
      
      private var _openCheckDecoderData:Boolean = false;
      
      private var _openDataLoader:Boolean = false;
      
      private var _rateAgent:RateAgent;
      
      private var _memAgent:MemoryAgent;
      
      private var _seeked:Boolean = false;
      
      private var _timeoutForWaiting:uint = 0;
      
      private var _timeoutForEmpty:uint = 0;
      
      public function get file() : IFile {
         return this._file;
      }
      
      public function get provider() : IProvider {
         return this._provider;
      }
      
      override public function get currentTime() : int {
         if(_decoder)
         {
            return _decoder.time;
         }
         return 0;
      }
      
      override public function get bufferTime() : int {
         if(this._file)
         {
            return this._file.bufferLength;
         }
         return 0;
      }
      
      override public function set openSelectPlay(param1:Boolean) : void {
         if(this._provider)
         {
            this._provider.openSelectPlay = param1;
         }
      }
      
      override public function bind(param1:IMovie, param2:IRender) : void {
         super.bind(param1,param2);
         this.createDecoder();
         this.createFile();
         _render.bind(this,_decoder,_movie);
         this._rateAgent.bind(_decoder,_render,this._provider,this._file,_movie);
         this._memAgent.bind(this._file,_decoder);
         setStatus(StatusEnum.ALREADY_READY);
      }
      
      override public function startLoad() : void {
         super.startLoad();
         _holder.runtimeData.retry4013 = 0;
         if(!_holder.hasStatus(StatusEnum.ALREADY_PLAY))
         {
            _decoder.play(null);
            this.seek(_startTime);
            this._rateAgent.startAutoAdjustRate();
            this._memAgent.start();
         }
         pause();
         setStatus(StatusEnum.ALREADY_START_LOAD);
      }
      
      override public function stopLoad() : void {
         super.stopLoad();
         this._openDataLoader = false;
         if(this._provider)
         {
            this._provider.stopLoading();
         }
      }
      
      override public function play() : void {
         if(!_holder.hasStatus(StatusEnum.ALREADY_PLAY))
         {
            super.play();
            this.openListenDecoder();
            if(!_holder.hasStatus(StatusEnum.ALREADY_START_LOAD))
            {
               _decoder.play(null);
               this.seek(_startTime);
               this._rateAgent.startAutoAdjustRate();
               this._memAgent.start();
            }
            resume();
         }
      }
      
      override public function replay() : void {
         if(_holder.hasStatus(StatusEnum.STOPED))
         {
            this.openListenDecoder();
            _decoder.play(null);
            super.replay();
         }
      }
      
      override public function seek(param1:uint) : void {
         if(_movie == null || !_movie.ready)
         {
            return;
         }
         super.seek(param1);
         this.openListenDecoder();
         _holder.runtimeData.retry4013 = 0;
         this._seeked = true;
         this._file.seek(_seekTime);
         this._provider.seek(_seekTime);
         _decoder.seek(_seekTime == 0?100:_seekTime);
         this._openCheckDecoderData = true;
         this._openDataLoader = true;
      }
      
      override public function stop(param1:EnumItem) : void {
         this._openCheckDecoderData = false;
         this._openDataLoader = false;
         if(this._provider)
         {
            this._provider.clear();
         }
         if(this._file)
         {
            this._file.clear();
         }
         if(_movie)
         {
            _movie.removeEventListener(MovieEvent.Evt_Ready,this.onMovieMetaReady);
         }
         if(this._timeoutForWaiting)
         {
            clearTimeout(this._timeoutForWaiting);
            this._timeoutForWaiting = 0;
         }
         this.closeListenDecoder();
         super.stop(param1);
      }
      
      override protected function selfStop(param1:EnumItem) : void {
         this._openCheckDecoderData = false;
         this._openDataLoader = false;
         if(this._provider)
         {
            this._provider.stopLoading();
         }
         if(_movie)
         {
            _movie.removeEventListener(MovieEvent.Evt_Ready,this.onMovieMetaReady);
         }
         if(this._timeoutForWaiting)
         {
            clearTimeout(this._timeoutForWaiting);
            this._timeoutForWaiting = 0;
         }
         this.closeListenDecoder();
         super.selfStop(param1);
      }
      
      override protected function stopedHandler() : void {
         if(_decoder)
         {
            _decoder.stop();
         }
         super.stopedHandler();
      }
      
      private function createDecoder() : void {
         if(_decoder)
         {
            this.closeListenDecoder();
            _decoder.destroy();
         }
         _decoder = new DataModeDecoder(_holder);
         if(_holder.runtimeData.playerUseType == PlayerUseTypeEnum.MAIN && _holder.runtimeData.playerType == PlayerTypeEnum.MAIN_STATION)
         {
            _decoder.bufferTime = Config.STREAM_LONG_BUFFER_TIME / 1000;
         }
         else
         {
            _decoder.bufferTime = Config.STREAM_NORMAL_BUFFER_TIME / 1000;
         }
      }
      
      private function openListenDecoder() : void {
         if(_decoder)
         {
            _decoder.addEventListener(NetStatusEvent.NET_STATUS,this.onDecoderNetStatus);
            _decoder.addEventListener(DecoderEvent.Evt_StatusChanged,this.onDecoderStatusChanged);
            this.updateStatusByDecoder();
         }
      }
      
      private function closeListenDecoder() : void {
         if(_decoder)
         {
            _decoder.removeEventListener(NetStatusEvent.NET_STATUS,this.onDecoderNetStatus);
            _decoder.removeEventListener(DecoderEvent.Evt_StatusChanged,this.onDecoderStatusChanged);
         }
      }
      
      private function createFile() : void {
         if(this._provider)
         {
            this._provider.removeEventListener(ProviderEvent.Evt_Failed,this.onFailed);
            this._provider.removeEventListener(ProviderEvent.Evt_StartLoad,this.onStartLoad);
            this._provider.destroy();
         }
         if(this._file)
         {
            this._file.destroy();
         }
         if((_holder.runtimeData.supportClientP2P) && (_holder.runtimeData.openClientP2P) && !_movie.member)
         {
            _holder.pingBack.stopFlashP2PFailedCDN();
            this._file = new ClientP2PFile();
            this._file.initialize(_holder,_movie,_decoder);
            this._provider = new ClientP2PProvider(this._file as IClientP2PFile,_decoder);
         }
         else if((_holder.runtimeData.supportFlashP2P) && (_holder.runtimeData.openFlashP2P) && !_movie.member)
         {
            _holder.pingBack.stopFlashP2PFailedCDN();
            this._file = new FlashP2PFile();
            this._file.initialize(_holder,_movie,_decoder);
            this._provider = new FlashP2PProvider(this._file as IFlashP2PFile,_decoder);
         }
         else
         {
            _holder.pingBack.startFlashP2PFailedCDN();
            this._file = new CoreFile();
            this._file.initialize(_holder,_movie,_decoder);
            this._provider = new CoreProvider(this._file as ICoreFile,_decoder);
         }
         
         this._provider.addEventListener(ProviderEvent.Evt_Failed,this.onFailed);
         this._provider.addEventListener(ProviderEvent.Evt_StartLoad,this.onStartLoad);
      }
      
      private function switchFile() : void {
         if(!_holder.hasStatus(StatusEnum.IDLE) && !_holder.hasStatus(StatusEnum.STOPPING) && !_holder.hasStatus(StatusEnum.STOPED) && !_holder.hasStatus(StatusEnum.FAILED))
         {
            this.createFile();
            this._rateAgent.bind(_decoder,_render,this._provider,this._file,_movie);
            this._memAgent.bind(this._file,_decoder);
            this.seek(_decoder.time + _decoder.bufferLength * 1000);
         }
      }
      
      private function onDecoderNetStatus(param1:NetStatusEvent) : void {
         var _loc2_:* = 0;
         var _loc3_:EnumItem = null;
         switch(param1.info.code)
         {
            case "NetStream.Buffer.Flush":
               _decoder.bufferTime = Config.STREAM_SHORT_BUFFER_TIME / 1000;
               if(_decoder.bufferLength > _decoder.bufferTime)
               {
                  break;
               }
            case "NetStream.Buffer.Empty":
               if(_decoder.bufferLength >= _decoder.bufferTime)
               {
                  break;
               }
               if(this._file.eof)
               {
                  this.selfStop(StopReasonEnum.STOP);
               }
               else
               {
                  _loc2_ = _holder.runtimeData.endTime;
                  _loc3_ = StopReasonEnum.REACH_ASSIGN;
                  if(_loc2_ == 0 && (_holder.runtimeData.skipTrailer))
                  {
                     _loc2_ = _movie.trailerTime;
                     _loc3_ = StopReasonEnum.SKIP_TRAILER;
                  }
                  if(_loc2_ > 0 && Math.abs(_decoder.time - _loc2_) < 1000)
                  {
                     this.selfStop(_loc3_);
                  }
                  else if((this._provider.loadingFailed) || _holder.runtimeData.retry4013 >= Config.RETRY_4013_MAX_RETRY)
                  {
                     this.onFailed();
                  }
                  else
                  {
                     if(_decoder.status != DecoderStatusEnum.STOPPED)
                     {
                        this.pushDataToDecoder();
                     }
                     if(_decoder.bufferLength < 1)
                     {
                        if(!this._seeked)
                        {
                           clearTimeout(this._timeoutForEmpty);
                           this._timeoutForEmpty = setTimeout(this.sendEmptyPingback,500);
                           dispatchEvent(new EngineEvent(EngineEvent.Evt_Stuck));
                        }
                        if(this._timeoutForWaiting)
                        {
                           clearTimeout(this._timeoutForWaiting);
                        }
                        this._timeoutForWaiting = setTimeout(this.waitingLongTime,Config.SCREEN_BLANK_MAX);
                     }
                  }
                  
               }
               break;
            case "NetStream.Buffer.Full":
               clearTimeout(this._timeoutForEmpty);
               this._timeoutForEmpty = 0;
               if(this._timeoutForWaiting)
               {
                  clearTimeout(this._timeoutForWaiting);
                  this._timeoutForWaiting = 0;
               }
               this._seeked = false;
               _holder.runtimeData.retry4013 = 0;
               if((this._file) && this._file is IFlashP2PFile)
               {
                  IFlashP2PFile(this._file).setTriggerEvent(1);
               }
               break;
            case "NetStream.Seek.Notify":
               if(_decoder.status != DecoderStatusEnum.STOPPED)
               {
                  this.pushDataToDecoder();
               }
               break;
         }
      }
      
      private function sendEmptyPingback() : void {
         clearTimeout(this._timeoutForEmpty);
         this._timeoutForEmpty = 0;
         _holder.runtimeData.bufferEmpty++;
         _holder.pingBack.sendError(4015);
         if((this._file) && this._file is IFlashP2PFile)
         {
            IFlashP2PFile(this._file).setTriggerEvent(0);
         }
      }
      
      private function onDecoderStatusChanged(param1:DecoderEvent) : void {
         this.updateStatusByDecoder();
      }
      
      private function onFailed(param1:Event = null) : void {
         var _loc2_:IFlashP2PProvider = null;
         this._openDataLoader = false;
         if(this._provider)
         {
            this._provider.stopLoading();
         }
         if(this._timeoutForWaiting)
         {
            clearTimeout(this._timeoutForWaiting);
            this._timeoutForWaiting = 0;
         }
         if(this._file is IClientP2PFile)
         {
            _log.info("client p2p failed, start switch file!");
            this._openCheckDecoderData = false;
            _holder.runtimeData.openClientP2P = false;
            this.closeListenDecoder();
            this.switchFile();
         }
         else if(this._file is IFlashP2PFile)
         {
            _loc2_ = this._provider as IFlashP2PProvider;
            if((_loc2_) && (_loc2_.isFatal))
            {
               _log.info("flash p2p failed, isFatal!");
               this.executeFailedHandler();
            }
            else
            {
               _log.info("flash p2p failed, start switch file! isPreload = " + _holder.isPreload + " tvid = " + _holder.movieModel.tvid);
               _holder.pingBack.flashP2PErro = true;
               this._openCheckDecoderData = false;
               _holder.runtimeData.openFlashP2P = false;
               this.closeListenDecoder();
               this.switchFile();
            }
         }
         else
         {
            this.executeFailedHandler();
         }
         
      }
      
      private function executeFailedHandler() : void {
         if(!_holder.runtimeData.useDataModel)
         {
            this._openCheckDecoderData = false;
            this.closeListenDecoder();
            setStatus(StatusEnum.FAILED);
         }
         else if(_decoder.bufferLength < _decoder.bufferTime || _decoder.bufferLength > 30)
         {
            this._openCheckDecoderData = false;
            this.closeListenDecoder();
            setStatus(StatusEnum.FAILED);
            dispatchEvent(new EngineEvent(EngineEvent.Evt_Error));
         }
         
      }
      
      private function onStartLoad(param1:Event) : void {
         if(_decoder.status == DecoderStatusEnum.SEEKING || _decoder.status == DecoderStatusEnum.WAITING)
         {
            if(this._timeoutForWaiting)
            {
               clearTimeout(this._timeoutForWaiting);
            }
            this._timeoutForWaiting = setTimeout(this.waitingLongTime,Config.SCREEN_BLANK_MAX);
         }
      }
      
      private function waitingLongTime() : void {
         clearTimeout(this._timeoutForWaiting);
         this._timeoutForWaiting = 0;
         _holder.pingBack.sendError(4013);
         _holder.runtimeData.errorCode = 4013;
         if(_holder.runtimeData.retry4013 < Config.RETRY_4013_MAX_RETRY)
         {
            _holder.runtimeData.retry4013++;
            this._provider.retry();
         }
         else
         {
            this.onFailed();
         }
      }
      
      private function onAudioTrackChanged(param1:RateAgentEvent) : void {
         if(!_holder.hasStatus(StatusEnum.IDLE) && !_holder.hasStatus(StatusEnum.STOPPING) && !_holder.hasStatus(StatusEnum.STOPED) && !_holder.hasStatus(StatusEnum.FAILED))
         {
            dispatchEvent(new EngineEvent(EngineEvent.Evt_AudioTrackSwitched,param1.data));
            this.prepareSwitchMediaData();
         }
      }
      
      private function onDefinitionChanged(param1:RateAgentEvent) : void {
         if(!_holder.hasStatus(StatusEnum.IDLE) && !_holder.hasStatus(StatusEnum.STOPPING) && !_holder.hasStatus(StatusEnum.STOPED) && !_holder.hasStatus(StatusEnum.FAILED))
         {
            dispatchEvent(new EngineEvent(EngineEvent.Evt_DefinitionSwitched,param1.data));
            if(int(param1.data) >= 0)
            {
               this.prepareSwitchMediaData();
            }
         }
      }
      
      private function prepareSwitchMediaData() : void {
         this._file.prepareSwitchMediaData();
         this._provider.prepareSwitchMediaData();
         if(_movie.ready)
         {
            this.onMovieMetaReady();
         }
         else
         {
            this._openCheckDecoderData = false;
            this._openDataLoader = false;
            _movie.addEventListener(MovieEvent.Evt_Ready,this.onMovieMetaReady);
         }
      }
      
      private function onMovieMetaReady(param1:Event = null) : void {
         var _loc2_:* = NaN;
         if(param1)
         {
            _movie.removeEventListener(MovieEvent.Evt_Ready,this.onMovieMetaReady);
         }
         if(!this._provider.loadingFailed && !_holder.hasStatus(StatusEnum.IDLE) && !_holder.hasStatus(StatusEnum.STOPPING) && !_holder.hasStatus(StatusEnum.STOPED) && !_holder.hasStatus(StatusEnum.FAILED))
         {
            this.openListenDecoder();
            _loc2_ = _decoder.time + _decoder.bufferLength * 1000;
            _movie.seek(_loc2_);
            if(_movie.curSegment.currentKeyframe)
            {
               _loc2_ = _movie.getSeekTime();
               this._file.seek(_loc2_);
               this._provider.seek(_loc2_);
            }
            else
            {
               _loc2_ = _movie.curSegment.startTime;
               this._file.seek(_loc2_);
               this._provider.seek(_loc2_);
               _decoder.seek(_loc2_ == 0?100:_loc2_);
            }
            this._openCheckDecoderData = true;
            this._openDataLoader = true;
         }
      }
      
      override protected function updateStatusByDecoder() : void {
         if(_decoder)
         {
            switch(_decoder.status)
            {
               case DecoderStatusEnum.PLAYING:
                  setStatus(StatusEnum.PLAYING);
                  break;
               case DecoderStatusEnum.PAUSED:
                  setStatus(StatusEnum.PAUSED);
                  break;
               case DecoderStatusEnum.SEEKING:
                  setStatus(StatusEnum.SEEKING);
                  break;
               case DecoderStatusEnum.WAITING:
                  setStatus(StatusEnum.WAITING);
                  break;
            }
         }
      }
      
      override protected function onTimer(param1:TimerEvent) : void {
         if((_holder.hasStatus(StatusEnum.ALREADY_READY)) && !_holder.hasStatus(StatusEnum.STOPED))
         {
            super.onTimer(param1);
            this.checkDecoderData();
            this.checkDataLoader();
         }
      }
      
      private function checkDecoderData() : void {
         var _loc1_:* = 0;
         if(this._openCheckDecoderData)
         {
            _loc1_ = 0;
            if(_holder.runtimeData.playerUseType == PlayerUseTypeEnum.MAIN && _holder.runtimeData.playerType == PlayerTypeEnum.MAIN_STATION)
            {
               _loc1_ = Config.STREAM_LONG_BUFFER_TIME;
            }
            else
            {
               _loc1_ = Config.STREAM_NORMAL_BUFFER_TIME;
            }
            if((_decoder) && (!(_decoder.status == DecoderStatusEnum.STOPPED)) && _decoder.bufferLength * 1000 < _loc1_ + 2000)
            {
               this.pushDataToDecoder();
            }
         }
      }
      
      private function checkDataLoader() : void {
         if(this._openDataLoader)
         {
            this._provider.drive();
         }
      }
      
      private function pushDataToDecoder() : void {
         var _loc1_:MediaData = null;
         if((this._file && this._provider) && (_decoder) && !this._file.eof)
         {
            _loc1_ = this._file.sequenceReadData();
            if(_loc1_)
            {
               DataModeDecoder(_decoder).appendData(_loc1_);
               _log.info("decoder bufferLength: " + _decoder.bufferLength * 1000);
            }
            if(this._file.eof)
            {
               DataModeDecoder(_decoder).endSequence();
            }
            if(this._provider.loadingFailed)
            {
               if(_decoder.bufferLength < _decoder.bufferTime || _decoder.bufferLength > 30)
               {
                  _log.debug("DMEngine pushDataToDecoder : provider is failed!");
                  this.onFailed();
               }
            }
         }
      }
      
      override public function destroy() : void {
         if((_holder) && (_holder.pingBack))
         {
            _holder.pingBack.stopFlashP2PFailedCDN();
         }
         this._memAgent.destroy();
         this._memAgent = null;
         this._rateAgent.removeEventListener(RateAgentEvent.Evt_AudioTrackChanged,this.onAudioTrackChanged);
         this._rateAgent.removeEventListener(RateAgentEvent.Evt_DefinitionChanged,this.onDefinitionChanged);
         this._rateAgent.destroy();
         this._rateAgent = null;
         if(this._provider)
         {
            this._provider.removeEventListener(ProviderEvent.Evt_Failed,this.onFailed);
            this._provider.removeEventListener(ProviderEvent.Evt_StartLoad,this.onStartLoad);
            this._provider.destroy();
            this._provider = null;
         }
         if(this._file)
         {
            this._file.destroy();
            this._file = null;
         }
         if(_movie)
         {
            _movie.removeEventListener(MovieEvent.Evt_Ready,this.onMovieMetaReady);
         }
         if(_decoder)
         {
            _decoder.removeEventListener(NetStatusEvent.NET_STATUS,this.onDecoderNetStatus);
            _decoder.removeEventListener(DecoderEvent.Evt_StatusChanged,this.onDecoderStatusChanged);
            _decoder.destroy();
            _decoder = null;
         }
         if(this._timeoutForWaiting)
         {
            clearTimeout(this._timeoutForWaiting);
            this._timeoutForWaiting = 0;
         }
         if(this._timeoutForEmpty)
         {
            clearTimeout(this._timeoutForEmpty);
            this._timeoutForEmpty = 0;
         }
         super.destroy();
      }
   }
}
